#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Manager.h"

#include "Class.h"
#include "Application.h"
#include "Window.h"
#include "String.h"
#include "TextItem.h"
#include "CommandProcessor.h"
#include "WindowSystem.h"
#include "Env.h"
#include "OrdColl.h"
#include "Box.h"
#include "MenuBar.h"
#include "CmdNo.h"
#include "Fields.h"
#include "Menu.h"
#include "View.h"
#include "IAC.h"
#include "System.h"

//---- Manager -----------------------------------------------------------------

NewAbstractMetaImpl(Manager,EvtHandler, (TP(name), TP(managers), TP(nextManager),
		TP(menubar), TP(menu), TP(firstHandler), TP(window),
		TE(wtype), TP(cmdp), TB(isopen), TB(wasopen)));

Manager::Manager(const char *t, MgrDismiss d, WindowType wt)
{
	Init(0, t, d, wt);
}

Manager::Manager(Manager *nm, const char *t, MgrDismiss d, WindowType wt)
{
	Init(nm, t, d, wt);
}

void Manager::Init(Manager *nm, const char *t, MgrDismiss d, WindowType wt)
{
	managers= 0;
	menu= 0;
	tvo= 0;
	window= 0;
	wtype= wt;
	wflags= eWinDefault;
	//if (nm == 0)
	//    nm= gApplication;
	nextManager= nm;
	cmdp= 0;
	menubar= 0;
	firstHandler= 0;
	dismiss= d;
	wasopen= TRUE;
	isopen= FALSE;

	name= 0;
	SetName(t);
}

Manager::~Manager()
{
	firstHandler= 0;
	if (nextManager) {
		if (nextManager->managers)
			nextManager->managers->RemovePtr(this);
		nextManager= 0;
	}
	SafeDelete(tvo);
	if (cmdp) {
		cmdp->RemoveObserver(this);
		SafeUnref(cmdp);
	}
	SafeDelete(menu);
	SafeDelete(window);
	SafeDelete(name);
	if (managers) {
		OrdCollection *tmp= managers;
		managers= 0;
		tmp->FreeAll();
		delete tmp;
	}
}

Point Manager::GetInitialPos(Point)
{
	return gPoint0;
}

Point Manager::GetInitialWindowPos()
{
	if (nextManager)
		return nextManager->GetInitialWindowPos();
	return gPoint0;
}

Point Manager::GetInitialWindowSize()
{
	return Env::GetValue("Document.Size", Point(500, 600));
}

void Manager::SetName(const char *nm)
{
	if (nm) {
		strreplace(&name, nm);
		SetWindowTitle(name);
	}
}

const char *Manager::GetName()
{
	return name;
}

u_long Manager::UniqueId()
{
	return (u_long)GetId();
}

bool Manager::IsEqual(Object *op)
{
	if (op->IsKindOf(Manager))
		return UniqueId() == ((Manager*)op)->UniqueId();
	return FALSE;
}

CommandProcessor *Manager::GetCmdP()
{
	if (cmdp == 0) {
		cmdp= MakeCmdProcessor();
		if (cmdp)
			cmdp->Ref();
		cmdp->AddObserver(this);
	}
	return cmdp;
}

void Manager::ClearCmdProcessor()
{
	SafeUnref(cmdp);
}

void Manager::SetCmdProcessor(CommandProcessor *c)
{
	if (c)
		c->Ref();
	SafeUnref(cmdp);
	cmdp= c;
}

CommandProcessor *Manager::MakeCmdProcessor()
{
	return new SingleCommandProcessor;
}

void Manager::AboutToOpen()
{
}

void Manager::OpenAt(VObject *fp, Point p, bool resize, bool block)
{
	isopen= wasopen= TRUE;
	MakeWindows();
	if (window) {
		AboutToOpen();
		window->OpenAt(p, fp, resize, block);
	}
}

int Manager::ShowAt(VObject *fp, Point p, bool resize, bool block)
{
	OpenAt(fp, p, resize, block);
	return cIdNone;
}

void Manager::Open()
{
	OpenAt(0, GetInitialWindowPos(), FALSE);
}

int Manager::ShowUnderMouse()
{
	return ShowAt(gWindow, gToken.Pos);
}

int Manager::ShowOnWindow(VObject *fp)
{
	if (fp)
		return ShowAt(fp, fp->GetExtent().Half());
	return ShowUnderMouse();
}

int Manager::Run()
{
	return 0;
}

bool Manager::Close()   // return TRUE if OK
{
	if (isopen) {
		Manager *mp;
		Iter next(MakeIterator());

		while (mp= (Manager*) next()) // try to close all managers
			if (! mp->Close())
				return FALSE;

		isopen= wasopen= FALSE;
		if (window)
			window->Close();
		if (nextManager)
			nextManager->RemoveManager(this);
	}
	return TRUE;
}

void Manager::Show()
{
	if (isopen) {
		if (window)
			window->Top();
	} else {
		if (window) {
			AboutToOpen();
			window->Open();
		} else
			Open();
		wasopen= isopen= TRUE;
		if (managers) {
			managers->ForEach(Manager,Unhide)();
		}
	}
}

void Manager::Unhide()
{
	if (wasopen) {
		if (window) {
			AboutToOpen();
			window->Open();
		} else
			Open();
		isopen= wasopen= TRUE;
		if (managers) {
			managers->ForEach(Manager,Unhide)();
		}
	}
}

void Manager::Hide()
{
	if (isopen) {
		isopen= wasopen= FALSE;
		if (managers) {
			managers->ForEach(Manager,HideAndRemember)();
		}
		if (window) {
			window->Close();
			window->UpdateEvent();
		}
	}
}

void Manager::Iconize()
{
	if (isopen) {
		isopen= wasopen= FALSE;
		if (managers) {
			managers->ForEach(Manager,HideAndRemember)();
		}
		if (window) {
			window->Iconize();
			window->UpdateEvent();
		}
	}
}

void Manager::HideAndRemember()
{
	if (isopen) {
		isopen= FALSE;
		wasopen= TRUE;
		if (managers) {
			managers->ForEach(Manager,HideAndRemember)();
		}
		if (window)
			window->Close();
	}
}

bool Manager::AboutToDismiss()
{
	if (firstHandler) {
		bool rc= firstHandler->KbdFocus(FALSE);
		if (!rc)
			return FALSE;
		firstHandler->KbdFocus(TRUE);
	}
	return TRUE;
}

bool Manager::Dismiss()
{
	if (AboutToDismiss()) {
		switch (dismiss) {
		case eMgrClose:
			return Close();

		case eMgrHide:
			Hide();
			return TRUE;
		
		case eMgrIconize:
			Iconize();
			return TRUE;
		}
	}
	return FALSE;
}

void Manager::AddManager(Manager *mp)
{
	if (mp) {
		if (managers == 0)
			managers= new OrdCollection;
		else
			managers->RemovePtr(mp);
		managers->Add(mp);
		mp->SetNextManager(this);
	}
}

Iterator *Manager::MakeIterator(void *placement)
{
	if (managers)
		return managers->MakeIterator(cIterForward, placement);
	return new(placement) Iterator0;
}

int Manager::Size()
{
	if (managers)
		return managers->Size();
	return 0;
}

Manager *Manager::FindManager(u_long i)
{
	Manager *dp;
	Iter next(MakeIterator());

	while (dp= (Manager*) next())
		if (dp->UniqueId() == i)
			return dp;
	return 0;
}

void Manager::RemoveManager(Manager *mp)
{
	if (gSystem && managers && managers->RemovePtr(mp))
		gSystem->AddCleanupObject(mp);
}

void Manager::SetNextManager(Manager *mp)
{
	nextManager= mp;
}

EvtHandler *Manager::GetNextHandler()
{
	return nextManager;
}

void Manager::MakeWindows()
{
	if (window == 0) {
		window= DoMakeWindows();
		if (window)
			window->SetNextManager(this);
		SetWindowTitle(GetName());
	}
}

Window *Manager::GetWindow()
{
	return window;
}

VObject *Manager::DoMakeContent()
{
	return 0;
}

VObject *Manager::DoMakeIconContent()
{
	return 0;
}

Window *Manager::DoMakeWindows()
{
	VObject *inner= DoMakeContent();
	if (inner) {
		menubar= DoMakeMenuBar();
		if (menubar)
			inner= new Expander(cIdNone, eVert, gPoint2, menubar, inner, 0);
		return new Window(this, inner, wtype, wflags, GetInitialWindowSize(), GetName());
	}
	return 0;
}

MenuBar *Manager::GetMenuBar()
{
	return menubar;
}

void Manager::SetWindowTitle(const char *t)
{
	if (window)
		window->SetTitle(t);
}

void Manager::SetOnDismiss(MgrDismiss d)
{
	dismiss= d;
}

//---- Menus -------------------------------------------------------------------

Menu *Manager::GetMenu()
{
	if (menu == 0) {
		menu= new Menu(cEDITMENU, "Edit");
		GetCmdP()->DoCreateMenu(menu);
		menu->AppendItems(  "-",
							"Cut@X",  cCUT,
							"Copy@C", cCOPY,
							"Paste@V",cPASTE,
							0);
	}
	return menu;
}

Menu *Manager::MakeMenu(int menuId)
{
	Menu *m= 0;

	switch (menuId) {
	case cHELPMENU:
		m= new Menu(cHELPMENU, "Help", FALSE);
		m->AppendItem("About", cABOUT);
		m->AppendItem("Help",  cHELP);
		break;

	case cMANAGERMENU:
		m= new Menu(cMANAGERMENU, "File");
		m->AppendItems(
				"New@N",      cNEW,
				"-",
				"Close@W",      cCLOSE,
				"Print ...@P",cPRINT,
				"-",
				"Quit@Q",     cQUIT,
				0);
		break;

	case cTOOLMENU:
		m= new Menu(cTOOLMENU, "Tool", FALSE);
		m->AppendItem("Close@W", cCLOSE);
		break;

	case cEDITMENU:
		m= new Menu(cEDITMENU, "Edit");
		GetCmdP()->DoCreateMenu(m);
		m->AppendItems("-",
				"Cut@X",          cCUT,
				"Copy@C",         cCOPY,
				"Paste@V",        cPASTE,
				"Paste File ...", cIMPORT,
				"Clear",          cCLEAR,
				"-",
				"Select All@A",   cSELECTALL,
				0);
		break;
		
	default:
		if (nextManager)
			m= nextManager->MakeMenu(menuId);
	}
	return m;
}

MenuBar *Manager::DoMakeMenuBar()
{
	MenuBar *mb= new MenuBar;

	mb->AddMenu(MakeMenu(cTOOLMENU));
	mb->AddMenu(MakeMenu(cEDITMENU));
	mb->AddMenuRight(MakeMenu(cHELPMENU));
	return mb;
}

void Manager::DoSetupMenu(Menu *m)
{
	EvtHandler::DoSetupMenu(m);
	switch (m->GetId()) {
	case cEDITMENU:
		GetCmdP()->DoSetupMenu(m);
		break;
	case cFILEMENU:
		m->EnableItems(cNEW, cCLOSE, cPRINT, 0);
		break;
	}
}

Command *Manager::DoMenuCommand(int cmd)
{
	switch(cmd) {
	case cCLOSE:
		Dismiss();
		break;
	case cCOLLAPSE:
		Iconize();
		break;
	case 4711:
		Hide();
		break;
	case 4712:
		Show();
		break;
	case cUNDO:
	case cREDO:
		GetCmdP()->DoMenuCommand(cmd);
		break;
	case cHELP:
		gRpc->Talk("helptool", "show %s", ClassName());
		break;
	default:
		return EvtHandler::DoMenuCommand(cmd);
	}
	return gNoChanges;
}

void Manager::Control(int i, int part, void *vp)
{
	switch (i) {
	case cCLOSE:
		Dismiss();
		break;
	case cCOLLAPSE:
		Iconize();
		break;
	case cHELP:
		gRpc->Talk("helptool", "show %s", ClassName());
		break;
	default:
		EvtHandler::Control(i, part, vp);
		break;
	}
}

void Manager::ExtCommand(int, char *req, char*, int, char *&, int &)
{
	if (strcmp(req, "close") == 0)
		Close();
	else if (strcmp(req, "iconize") == 0)
		Iconize();
	else if (strcmp(req, "show") == 0)
		Show();
}

void Manager::InputKbd(Token &t)
{
	int cmd= cIdNone;
	Command *cp= 0;

	if (t.IsKey() && t.Code == '\r' && GetDefaultButton()) {
		if (AboutToDismiss())
			GetDefaultButton()->Flush();
		return;
	}
	if (t.Flags & eFlgMetaKey) {
		if (menu)
			cmd= menu->CheckKey(t);
		if (cmd == cIdNone && menubar)
			cmd= menubar->CheckKey(t);
		if (cmd != cIdNone) {
			if (firstHandler)
				cp= firstHandler->DoMenuCommand(cmd);
		} else if (window)
			window->Bell();
	} else if (cp == 0 && firstHandler)
		cp= firstHandler->Input(t.Pos, t, window);
	if (cp)
		PerformCommand(cp);
}

bool Manager::SetFirstHandler(EvtHandler *focus)
{
	if (focus != firstHandler) {
		if (firstHandler == 0 || firstHandler->KbdFocus(FALSE)) {
			firstHandler= 0;
			if (focus) {
				if (focus->KbdFocus(TRUE))
					firstHandler= focus;
			}
			return TRUE;
		}
		return FALSE;
	}
	return TRUE;
}

TextViewOverlay *Manager::GetTextViewOverlay()
{
	if (tvo == 0)
		tvo= new TextViewOverlay;
	return tvo;
}

void Manager::SendDown(int i, int part, void *val)
{
	if (managers) {
		managers->ForEach(Manager,SendDown)(i, part, val);
	}
}

void Manager::PerformCommand(Command *cmd)
{
	GetCmdP()->PerformCommand(cmd);
}

//---- Default Button ----------------------------------------------------------

static bool FindDfltButton(Object*, Object *op, void *vp)
{
	VObject *vop= (VObject*) op;
	if (vop->IsKindOf(ActionButton) && vop->TestFlag(eActionDefaultButton)
							&& vop->Enabled() /* && vop->IsOpen() */) {
		*((Button**) vp)= (ActionButton*) vop;
		return TRUE;
	}
	return FALSE;
}

Button *Manager::GetDefaultButton()
{
	Button *dfltButton= 0;
	if (window->GetViewedVObject())
		window->GetViewedVObject()->Detect(FindDfltButton, &dfltButton);
	return dfltButton;
}

//---- Focus -------------------------------------------------------------------

struct FindFocus {
	EvtHandler *focus;
	VObject *first, *last, *prev, *next;
	bool found;

	FindFocus(EvtHandler *f)
		{ first= last= prev= next= 0; found= FALSE; focus= f; }
};

static bool FindFocusItem(Object*, Object *op, void *vp)
{
	register FindFocus *ff= (FindFocus*) vp;
	VObject *vop= (VObject*) op;

	if (vop->WantsKbdFocus() && vop->Enabled() /* && vop->IsOpen() */) {
		if (ff->first == 0)
			ff->first= vop;
		if (ff->found && ff->next == 0)
			ff->next= vop;
		if (vop == ff->focus)
			ff->found= TRUE;
		if (! ff->found)
			ff->prev= vop;
		ff->last= vop;
	}
	return FALSE;
}

void Manager::TabFields(Token &t)
{
	FindFocus ff(firstHandler);

	window->GetViewedVObject()->Detect(FindFocusItem, &ff);
	if (ff.prev == 0)
		ff.prev= ff.last;
	if (ff.next == 0)
		ff.next= ff.first;

	if (firstHandler == 0) // take the first enabled item
		SetFirstHandler(ff.first);
	else if (t.Flags & eFlgShiftKey)
		SetFirstHandler(ff.prev);
	else
		SetFirstHandler(ff.next);
}

//---- Inspector ---------------------------------------------------------------

void Manager::CollectParts(Collection* col)
{
	EvtHandler::CollectParts(col);
	if (menu)
		col->Add(menu);
	if (cmdp)
		col->Add(cmdp);
	if (managers)
		col->Add(managers);
	if (window)
		col->Add(window);
}

void Manager::InspectorId(char *buf, int bufSize)
{
	if (GetName())
		strn0cpy(buf, GetName(), bufSize);
	else
		EvtHandler::InspectorId(buf, bufSize);
}

